/*-------------------------------------------------------------------------
 *
 * fe-lobj.c
 *	  前端大对象接口
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/interfaces/libpq/fe-lobj.c
 *
 *-------------------------------------------------------------------------
 */

#ifdef WIN32
/*
 *	由于 unlink/rename 在 port.h 中被 #define（通过 postgres_fe.h），
 *	在 MS C 中必须首先包含 io.h。这里同样适用于所有 WIN32。
 */
#include <io.h>
#endif

#include "postgres_fe.h"

#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
#endif

#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>

#include "libpq-fe.h"
#include "libpq-int.h"
#include "libpq/libpq-fs.h"		/* 必须在 sys/stat.h 之后 */
#include "port/pg_bswap.h"

#define LO_BUFSIZE		  8192

static int	fc_lo_initialize(PGconn *fc_conn);
static Oid	fc_lo_import_internal(PGconn *fc_conn, const char *fc_filename, Oid fc_oid);
static pg_int64 fc_lo_hton64(pg_int64 fc_host64);
static pg_int64 fc_lo_ntoh64(pg_int64 fc_net64);

/*
 * lo_open
 *	  打开一个已有的大对象
 *
 * 返回用于后续 lo_* 调用的文件描述符
 * 失败时返回 -1。
 */
int lo_open(PGconn *fc_conn, Oid fc_lobjId, int fc_mode)
{
	int			fc_fd;
	int			fc_result_len;
	PQArgBlock	fc_argv[2];
	PGresult   *fc_res;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_lobjId;

	fc_argv[1].isint = 1;
	fc_argv[1].len = 4;
	fc_argv[1].u.integer = fc_mode;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_open, &fc_fd, &fc_result_len, 1, fc_argv, 2);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_fd;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_close
 *	  关闭一个已有的大对象
 *
 * 成功时返回 0
 * 失败时返回 -1。
 */
int lo_close(PGconn *fc_conn, int fc_fd)
{
	PQArgBlock	fc_argv[1];
	PGresult   *fc_res;
	int			fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;
	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_close,
			   &fc_retval, &fc_result_len, 1, fc_argv, 1);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_truncate
 *	  将一个已有的大对象截断到给定的大小
 *
 * 成功时返回 0
 * 失败时返回 -1
 */
int lo_truncate(PGconn *fc_conn, int fc_fd, size_t fc_len)
{
	PQArgBlock	fc_argv[2];
	PGresult   *fc_res;
	int			fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	/* 必须动态检查，因为在 8.3 之前没有这个 */
	if (fc_conn->lobjfuncs->fn_lo_truncate == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_truncate");
		return -1;
	}

	/*
	 * 很久以前，有人认为声明这个函数接受 size_t 是个好主意
	 * 但底层后端函数仅接受一个有符号的 int32 长度。
	 * 因此，如果给定值溢出 int32，就抛出错误。
	 * （一个可能的替代方案是自动重定向调用到 lo_truncate64；
	 * 但如果调用者想依赖于该后端函数可用，他可以自己调用 lo_truncate64。）
	 */
	if (fc_len > (size_t) INT_MAX)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("argument of lo_truncate exceeds integer range\n"));
		return -1;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_argv[1].isint = 1;
	fc_argv[1].len = 4;
	fc_argv[1].u.integer = (int) fc_len;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_truncate,
			   &fc_retval, &fc_result_len, 1, fc_argv, 2);

	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_truncate64
 *	  将一个已有的大对象截断到给定的大小
 *
 * 成功时返回 0
 * 失败时返回 -1
 */
int lo_truncate64(PGconn *fc_conn, int fc_fd, pg_int64 fc_len)
{
	PQArgBlock	fc_argv[2];
	PGresult   *fc_res;
	int			fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	if (fc_conn->lobjfuncs->fn_lo_truncate64 == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_truncate64");
		return -1;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_len = fc_lo_hton64(fc_len);
	fc_argv[1].isint = 0;
	fc_argv[1].len = 8;
	fc_argv[1].u.ptr = (int *) &fc_len;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_truncate64,
			   &fc_retval, &fc_result_len, 1, fc_argv, 2);

	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_read
 *	  将 len 字节的大对象读入 buf
 *
 * 返回读取的字节数，失败时返回 -1。
 * 调用者必须分配足够的空间来存放返回的结果
 */

int lo_read(PGconn *fc_conn, int fc_fd, char *fc_buf, size_t fc_len)
{
	PQArgBlock	fc_argv[2];
	PGresult   *fc_res;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	/*
	 * 很久以前，有人认为声明这个函数接受 size_t 是个好主意
	 * 但底层后端函数仅接受一个有符号的 int32 长度。
	 * 因此，如果给定值溢出 int32，就抛出错误。
	 */
	if (fc_len > (size_t) INT_MAX)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("argument of lo_read exceeds integer range\n"));
		return -1;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_argv[1].isint = 1;
	fc_argv[1].len = 4;
	fc_argv[1].u.integer = (int) fc_len;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_read,
			   (void *) fc_buf, &fc_result_len, 0, fc_argv, 2);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_result_len;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_write
 *	  将 len 字节的 buf 写入大对象 fd
 *
 * 返回写入的字节数，失败时返回 -1。
 */
int lo_write(PGconn *fc_conn, int fc_fd, const char *fc_buf, size_t fc_len)
{
	PQArgBlock	fc_argv[2];
	PGresult   *fc_res;
	int			fc_result_len;
	int			fc_retval;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	/*
	 * 很久以前，有人认为声明这个函数接受 size_t 是个好主意
	 * 但底层后端函数仅接受一个有符号的 int32 长度。
	 * 因此，如果给定值溢出 int32，就抛出错误。
	 */
	if (fc_len > (size_t) INT_MAX)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("argument of lo_write exceeds integer range\n"));
		return -1;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_argv[1].isint = 0;
	fc_argv[1].len = (int) fc_len;
	fc_argv[1].u.ptr = (int *) unconstify(char *, fc_buf);

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_write,
			   &fc_retval, &fc_result_len, 1, fc_argv, 2);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_lseek
 *	  更改大对象的当前读取或写入位置
 */
int lo_lseek(PGconn *fc_conn, int fc_fd, int fc_offset, int fc_whence)
{
	PQArgBlock	fc_argv[3];
	PGresult   *fc_res;
	int			fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_argv[1].isint = 1;
	fc_argv[1].len = 4;
	fc_argv[1].u.integer = fc_offset;

	fc_argv[2].isint = 1;
	fc_argv[2].len = 4;
	fc_argv[2].u.integer = fc_whence;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_lseek,
			   &fc_retval, &fc_result_len, 1, fc_argv, 3);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_lseek64
 *	  更改大对象的当前读取或写入位置
 */
pg_int64
lo_lseek64(PGconn *fc_conn, int fc_fd, pg_int64 fc_offset, int fc_whence)
{
	PQArgBlock	fc_argv[3];
	PGresult   *fc_res;
	pg_int64	fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	if (fc_conn->lobjfuncs->fn_lo_lseek64 == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_lseek64");
		return -1;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_offset = fc_lo_hton64(fc_offset);
	fc_argv[1].isint = 0;
	fc_argv[1].len = 8;
	fc_argv[1].u.ptr = (int *) &fc_offset;

	fc_argv[2].isint = 1;
	fc_argv[2].len = 4;
	fc_argv[2].u.integer = fc_whence;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_lseek64,
			   (void *) &fc_retval, &fc_result_len, 0, fc_argv, 3);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK && fc_result_len == 8)
	{
		PQclear(fc_res);
		return fc_lo_ntoh64(fc_retval);
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_creat
 *	  创建一个新的大对象
 * 模式被忽略（从前曾有用途）
 *
 * 返回新创建的大对象的 oid，或
 * 在失败时返回 InvalidOid
 */
Oid lo_creat(PGconn *fc_conn, int fc_mode)
{
	PQArgBlock	fc_argv[1];
	PGresult   *fc_res;
	int			fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return InvalidOid;

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_mode;
	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_creat,
			   &fc_retval, &fc_result_len, 1, fc_argv, 1);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return (Oid) fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return InvalidOid;
	}
}

/*
 * lo_create
 *	  创建一个新的大对象
 * 如果 lobjId 不是 InvalidOid，它指定要（尝试）创建的 OID
 *
 * 返回新创建的大对象的 oid，或
 * 在失败时返回 InvalidOid
 */
Oid lo_create(PGconn *fc_conn, Oid fc_lobjId)
{
	PQArgBlock	fc_argv[1];
	PGresult   *fc_res;
	int			fc_retval;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return InvalidOid;

	/* 必须动态检查，因为在 8.1 之前没有这个 */
	if (fc_conn->lobjfuncs->fn_lo_create == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_create");
		return InvalidOid;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_lobjId;
	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_create,
			   &fc_retval, &fc_result_len, 1, fc_argv, 1);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return (Oid) fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return InvalidOid;
	}
}


/*
 * lo_tell
 *	  返回大对象的当前寻址位置
 */
int lo_tell(PGconn *fc_conn, int fc_fd)
{
	int			fc_retval;
	PQArgBlock	fc_argv[1];
	PGresult   *fc_res;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_tell,
			   &fc_retval, &fc_result_len, 1, fc_argv, 1);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_tell64
 *	  返回大对象的当前寻址位置
 */
pg_int64
lo_tell64(PGconn *fc_conn, int fc_fd)
{
	pg_int64	fc_retval;
	PQArgBlock	fc_argv[1];
	PGresult   *fc_res;
	int			fc_result_len;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	if (fc_conn->lobjfuncs->fn_lo_tell64 == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_tell64");
		return -1;
	}

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_fd;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_tell64,
			   (void *) &fc_retval, &fc_result_len, 0, fc_argv, 1);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK && fc_result_len == 8)
	{
		PQclear(fc_res);
		return fc_lo_ntoh64(fc_retval);
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_unlink
 *	  删除一个文件
 */

int lo_unlink(PGconn *fc_conn, Oid fc_lobjId)
{
	PQArgBlock	fc_argv[1];
	PGresult   *fc_res;
	int			fc_result_len;
	int			fc_retval;

	if (fc_lo_initialize(fc_conn) < 0)
		return -1;

	fc_argv[0].isint = 1;
	fc_argv[0].len = 4;
	fc_argv[0].u.integer = fc_lobjId;

	fc_res = PQfn(fc_conn, fc_conn->lobjfuncs->fn_lo_unlink,
			   &fc_retval, &fc_result_len, 1, fc_argv, 1);
	if (PQresultStatus(fc_res) == PGRES_COMMAND_OK)
	{
		PQclear(fc_res);
		return fc_retval;
	}
	else
	{
		PQclear(fc_res);
		return -1;
	}
}

/*
 * lo_import -
 *	  导入一个文件作为（反转）大对象。
 *
 * 成功时返回该对象的oid，
 * 失败时返回InvalidOid
 */

Oid lo_import(PGconn *fc_conn, const char *fc_filename)
{
	return fc_lo_import_internal(fc_conn, fc_filename, InvalidOid);
}

/*
 * lo_import_with_oid -
 *	  导入一个文件作为（反转）大对象。
 *	  可以指定大对象id。
 *
 * 成功时返回该对象的oid，
 * 失败时返回InvalidOid
 */

Oid lo_import_with_oid(PGconn *fc_conn, const char *fc_filename, Oid fc_lobjId)
{
	return fc_lo_import_internal(fc_conn, fc_filename, fc_lobjId);
}

static Oid fc_lo_import_internal(PGconn *fc_conn, const char *fc_filename, Oid fc_oid)
{
	int			fc_fd;
	int			fc_nbytes,
				fc_tmp;
	char		fc_buf[LO_BUFSIZE];
	Oid			fc_lobjOid;
	int			fc_lobj;
	char		fc_sebuf[PG_STRERROR_R_BUFLEN];

	if (fc_conn == NULL)
		return InvalidOid;

	/* 由于这是查询周期的开始，重置错误状态 */
	pqClearConnErrorState(fc_conn);

	/*
	 * 打开要读取的文件
	 */
	fc_fd = open(fc_filename, O_RDONLY | PG_BINARY, 0666);
	if (fc_fd < 0)
	{							/* 错误 */
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("could not open file \"%s\": %s\n"),
						  fc_filename, strerror_r(errno, fc_sebuf, sizeof(fc_sebuf)));
		return InvalidOid;
	}

	/*
	 * 创建一个反转对象
	 */
	if (fc_oid == InvalidOid)
		fc_lobjOid = lo_creat(fc_conn, INV_READ | INV_WRITE);
	else
		fc_lobjOid = lo_create(fc_conn, fc_oid);

	if (fc_lobjOid == InvalidOid)
	{
		/* 我们假设lo_create()已经设置了合适的错误消息 */
		(void) close(fc_fd);
		return InvalidOid;
	}

	fc_lobj = lo_open(fc_conn, fc_lobjOid, INV_WRITE);
	if (fc_lobj == -1)
	{
		/* 我们假设lo_open()已经设置了合适的错误消息 */
		(void) close(fc_fd);
		return InvalidOid;
	}

	/*
	 * 从文件中读取并写入到大对象
	 */
	while ((fc_nbytes = read(fc_fd, fc_buf, LO_BUFSIZE)) > 0)
	{
		fc_tmp = lo_write(fc_conn, fc_lobj, fc_buf, fc_nbytes);
		if (fc_tmp != fc_nbytes)
		{
			/*
			 * 如果lo_write()失败，我们现在处于一个中止的事务中，
			 * 所以不需要lo_close(); 此外，如果我们尝试它，
			 * 我们将用无用的结果覆盖有用的错误结果。因此，
			 * 只需门锁紧，离开城镇。
			 */
			(void) close(fc_fd);
			return InvalidOid;
		}
	}

	if (fc_nbytes < 0)
	{
		/* 我们必须在设置errorMessage之前执行lo_close */
		int			fc_save_errno = errno;

		(void) lo_close(fc_conn, fc_lobj);
		(void) close(fc_fd);
		/* 故意覆盖lo_close中的任何错误 */
		pqClearConnErrorState(fc_conn);
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("could not read from file \"%s\": %s\n"),
						  fc_filename,
						  strerror_r(fc_save_errno, fc_sebuf, sizeof(fc_sebuf)));
		return InvalidOid;
	}

	(void) close(fc_fd);

	if (lo_close(fc_conn, fc_lobj) != 0)
	{
		/* 我们假设lo_close()已经设置了合适的错误消息 */
		return InvalidOid;
	}

	return fc_lobjOid;
}

/*
 * lo_export -
 *	  导出一个（反转）大对象。
 * 失败时返回-1，成功时返回1
 */
int lo_export(PGconn *fc_conn, Oid fc_lobjId, const char *fc_filename)
{
	int			fc_result = 1;
	int			fc_fd;
	int			fc_nbytes,
				fc_tmp;
	char		fc_buf[LO_BUFSIZE];
	int			fc_lobj;
	char		fc_sebuf[PG_STRERROR_R_BUFLEN];

	/*
	 * 打开大对象。
	 */
	fc_lobj = lo_open(fc_conn, fc_lobjId, INV_READ);
	if (fc_lobj == -1)
	{
		/* 我们假设lo_open()已经设置了合适的错误消息 */
		return -1;
	}

	/*
	 * 创建要写入的文件
	 */
	fc_fd = open(fc_filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
	if (fc_fd < 0)
	{
		/* 我们必须在设置errorMessage之前执行lo_close */
		int			fc_save_errno = errno;

		(void) lo_close(fc_conn, fc_lobj);
		/* 故意覆盖lo_close中的任何错误 */
		pqClearConnErrorState(fc_conn);
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("could not open file \"%s\": %s\n"),
						  fc_filename,
						  strerror_r(fc_save_errno, fc_sebuf, sizeof(fc_sebuf)));
		return -1;
	}

	/*
	 * 从大对象中读取并写入到文件
	 */
	while ((fc_nbytes = lo_read(fc_conn, fc_lobj, fc_buf, LO_BUFSIZE)) > 0)
	{
		fc_tmp = write(fc_fd, fc_buf, fc_nbytes);
		if (fc_tmp != fc_nbytes)
		{
			/* 我们必须在设置errorMessage之前执行lo_close */
			int			fc_save_errno = errno;

			(void) lo_close(fc_conn, fc_lobj);
			(void) close(fc_fd);
			/* 故意覆盖lo_close中的任何错误 */
			pqClearConnErrorState(fc_conn);
			appendPQExpBuffer(&fc_conn->errorMessage,
							  libpq_gettext("could not write to file \"%s\": %s\n"),
							  fc_filename,
							  strerror_r(fc_save_errno, fc_sebuf, sizeof(fc_sebuf)));
			return -1;
		}
	}

	/*
	 * 如果lo_read()失败，我们现在处于一个中止的事务中，
	 * 所以不需要lo_close(); 此外，如果我们尝试它，
	 * 我们将用无用的结果覆盖有用的错误结果。因此，
	 * 如果我们得到了失败结果，跳过lo_close()。
	 */
	if (fc_nbytes < 0 ||
		lo_close(fc_conn, fc_lobj) != 0)
	{
		/* 假设lo_read()或lo_close()留下了合适的错误消息 */
		fc_result = -1;
	}

	/* 如果我们已经失败，不要用关闭错误覆盖该消息 */
	if (close(fc_fd) != 0 && fc_result >= 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("could not write to file \"%s\": %s\n"),
						  fc_filename, strerror_r(errno, fc_sebuf, sizeof(fc_sebuf)));
		fc_result = -1;
	}

	return fc_result;
}


/*
 * lo_initialize
 *
 * 为现有连接上的新大对象操作进行初始化。
 * 如果成功返回0，失败返回-1。
 *
 * 如果我们之前没有这样做，我们将从pg_proc收集
 * 所有大对象操作所需的函数OID。
 */
static int fc_lo_initialize(PGconn *fc_conn)
{
	PGresult   *fc_res;
	PGlobjfuncs *fc_lobjfuncs;
	int			fc_n;
	const char *fc_query;
	const char *fc_fname;
	Oid			fc_foid;

	/* 没有连接我们无法做任何事情 */
	if (fc_conn == NULL)
		return -1;

	/* 由于这是查询周期的开始，重置错误状态 */
	pqClearConnErrorState(fc_conn);

	/* 如果我们已经收集了信息，就没有其他事情可做 */
	if (fc_conn->lobjfuncs != NULL)
		return 0;

	/*
	 * 分配结构以保存函数OID。我们不会在成功填充之前
	 * 将其存储到PGconn中。
	 */
	fc_lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
	if (fc_lobjfuncs == NULL)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return -1;
	}
	MemSet((char *) fc_lobjfuncs, 0, sizeof(PGlobjfuncs));

	/*
	 * 执行查询一次获取所有函数。 （在较旧的服务器版本中，
	 * 并非所有函数都可能存在。）
	 */
	fc_query = "select proname, oid from pg_catalog.pg_proc "
		"where proname in ("
		"'lo_open', "
		"'lo_close', "
		"'lo_creat', "
		"'lo_create', "
		"'lo_unlink', "
		"'lo_lseek', "
		"'lo_lseek64', "
		"'lo_tell', "
		"'lo_tell64', "
		"'lo_truncate', "
		"'lo_truncate64', "
		"'loread', "
		"'lowrite') "
		"and pronamespace = (select oid from pg_catalog.pg_namespace "
		"where nspname = 'pg_catalog')";

	fc_res = PQexec(fc_conn, fc_query);
	if (fc_res == NULL)
	{
		free(fc_lobjfuncs);
		return -1;
	}

	if (fc_res->resultStatus != PGRES_TUPLES_OK)
	{
		free(fc_lobjfuncs);
		PQclear(fc_res);
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("query to initialize large object functions did not return data\n"));
		return -1;
	}

	/*
	 * 检查结果并将OID放入结构体中
	 */
	for (fc_n = 0; fc_n < PQntuples(fc_res); fc_n++)
	{
		fc_fname = PQgetvalue(fc_res, fc_n, 0);
		fc_foid = (Oid) atoi(PQgetvalue(fc_res, fc_n, 1));
		if (strcmp(fc_fname, "lo_open") == 0)
			fc_lobjfuncs->fn_lo_open = fc_foid;
		else if (strcmp(fc_fname, "lo_close") == 0)
			fc_lobjfuncs->fn_lo_close = fc_foid;
		else if (strcmp(fc_fname, "lo_creat") == 0)
			fc_lobjfuncs->fn_lo_creat = fc_foid;
		else if (strcmp(fc_fname, "lo_create") == 0)
			fc_lobjfuncs->fn_lo_create = fc_foid;
		else if (strcmp(fc_fname, "lo_unlink") == 0)
			fc_lobjfuncs->fn_lo_unlink = fc_foid;
		else if (strcmp(fc_fname, "lo_lseek") == 0)
			fc_lobjfuncs->fn_lo_lseek = fc_foid;
		else if (strcmp(fc_fname, "lo_lseek64") == 0)
			fc_lobjfuncs->fn_lo_lseek64 = fc_foid;
		else if (strcmp(fc_fname, "lo_tell") == 0)
			fc_lobjfuncs->fn_lo_tell = fc_foid;
		else if (strcmp(fc_fname, "lo_tell64") == 0)
			fc_lobjfuncs->fn_lo_tell64 = fc_foid;
		else if (strcmp(fc_fname, "lo_truncate") == 0)
			fc_lobjfuncs->fn_lo_truncate = fc_foid;
		else if (strcmp(fc_fname, "lo_truncate64") == 0)
			fc_lobjfuncs->fn_lo_truncate64 = fc_foid;
		else if (strcmp(fc_fname, "loread") == 0)
			fc_lobjfuncs->fn_lo_read = fc_foid;
		else if (strcmp(fc_fname, "lowrite") == 0)
			fc_lobjfuncs->fn_lo_write = fc_foid;
	}

	PQclear(fc_res);

	/*
	 * 最后检查我们是否获得了所有所需的大对象接口函数
	 * （那些在石器时代之后添加的函数仅在使用时进行检查）
	 */
	if (fc_lobjfuncs->fn_lo_open == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_open");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_close == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_close");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_creat == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_creat");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_unlink == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_unlink");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_lseek == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_lseek");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_tell == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lo_tell");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_read == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "loread");
		free(fc_lobjfuncs);
		return -1;
	}
	if (fc_lobjfuncs->fn_lo_write == 0)
	{
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("cannot determine OID of function %s\n"),
						  "lowrite");
		free(fc_lobjfuncs);
		return -1;
	}

	/*
	 * 将结构体放入连接控制中
	 */
	fc_conn->lobjfuncs = fc_lobjfuncs;
	return 0;
}

/*
 * lo_hton64
 *	  将64位整数从主机字节序转换为网络字节序
 */
static pg_int64
fc_lo_hton64(pg_int64 fc_host64)
{
	union
	{
		pg_int64	i64;
		uint32		i32[2];
	}			fc_swap;
	uint32		fc_t;

	/* 高位先行，因为我们使用的是MSB优先 */
	fc_t = (uint32) (fc_host64 >> 32);
	fc_swap.i32[0] = pg_hton32(fc_t);

	/* 现在是低位 */
	fc_t = (uint32) fc_host64;
	fc_swap.i32[1] = pg_hton32(fc_t);

	return fc_swap.i64;
}

/*
 * lo_ntoh64
 *	  将64位整数从网络字节序转换为主机字节序
 */
static pg_int64
fc_lo_ntoh64(pg_int64 fc_net64)
{
	union
	{
		pg_int64	i64;
		uint32		i32[2];
	}			fc_swap;
	pg_int64	fc_result;

	fc_swap.i64 = fc_net64;

	fc_result = (uint32) pg_ntoh32(fc_swap.i32[0]);
	fc_result <<= 32;
	fc_result |= (uint32) pg_ntoh32(fc_swap.i32[1]);

	return fc_result;
}
